package com.zwps.common.controller.config;

import com.zwps.common.cache.CacheService;
import com.zwps.common.controller.shiro.modular.MyModularRealmAuthenticator;
import com.zwps.common.controller.shiro.realm.ShiroCasRealm;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.cas.CasFilter;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

import com.zwps.common.controller.config.properties.ShiroFilterUrlConfig;
import com.zwps.common.controller.constant.ShiroConstant;
import com.zwps.common.controller.constant.SwaggerConstant;
import com.zwps.common.controller.shiro.filter.CustomShiroFilterFactoryBean;
import com.zwps.common.controller.shiro.filter.JwtFilter;
import com.zwps.common.controller.shiro.realm.ShiroRealm;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.Filter;
import java.util.*;

@Slf4j
@Configuration
public class ShiroConfig {

    @Autowired
    private Environment env;

    @Autowired
    private ShiroFilterUrlConfig shiroFilterUrlConfig;

    @Value("${application.cas.serviceOne}")
    private String casServerUrlPrefix;

    @Value("${application.cas.casService}")
    private String casService;


    @Autowired
    private CacheService cacheService;
//    @Value("${application.cas.redirectUrl}")
//    private  String redirectUrl;
    /**
     * Filter Chain定义说明
     *
     * 1、一个URL可以配置多个Filter，使用逗号分隔 2、当设置多个过滤器时，全部验证通过，才视为通过 3、部分过滤器可指定参数，如perms，roles
     */
    @Bean("shiroFilterFactoryBean")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager, CasFilter casFilter) {
        CustomShiroFilterFactoryBean shiroFilterFactoryBean = new CustomShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 拦截器
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        List<String> pathAnonList = shiroFilterUrlConfig.getFilterPathAnon();
        if (pathAnonList != null && !pathAnonList.isEmpty()) {
            for (String path : pathAnonList) {
                filterChainDefinitionMap.put(path, ShiroConstant.FILTER_NAME_ANON);
            }
        }

        // 配置不会被拦截的链接 顺序判断
        SwaggerConstant.addSwaggerFilterUrl(filterChainDefinitionMap);

        filterChainDefinitionMap.put("/", ShiroConstant.FILTER_NAME_ANON);
        filterChainDefinitionMap.put("/**/*.js", ShiroConstant.FILTER_NAME_ANON);
        filterChainDefinitionMap.put("/**/*.css", ShiroConstant.FILTER_NAME_ANON);
        filterChainDefinitionMap.put("/**/*.html", ShiroConstant.FILTER_NAME_ANON);
        filterChainDefinitionMap.put("/**/*.svg", ShiroConstant.FILTER_NAME_ANON);
        filterChainDefinitionMap.put("/**/*.pdf", ShiroConstant.FILTER_NAME_ANON);
        filterChainDefinitionMap.put("/**/*.jpg", ShiroConstant.FILTER_NAME_ANON);
        filterChainDefinitionMap.put("/**/*.png", ShiroConstant.FILTER_NAME_ANON);
        filterChainDefinitionMap.put("/**/*.ico", ShiroConstant.FILTER_NAME_ANON);

//		filterChainDefinitionMap.put("/user2/**", ShiroConstant.FILTER_NAME_ANON);
        // 这个如果不加会导致不能正常访问spring默认的错误接口
        filterChainDefinitionMap.put("/error", ShiroConstant.FILTER_NAME_ANON);

        filterChainDefinitionMap.put("/druid/**", ShiroConstant.FILTER_NAME_ANON);

        // 性能监控 TODO: 存在安全漏洞泄露TOEKN（durid连接池也有）
        filterChainDefinitionMap.put("/actuator/**", ShiroConstant.FILTER_NAME_ANON);

        // 添加自己的过滤器并且取名为jwt
        Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
        // 如果cloudServer为空 则说明是单体 需要加载跨域配置【微服务跨域切换】
        shiroFilterFactoryBean.setFilters(filterMap);
        // <!-- 过滤链定义，从上向下顺序执行，一般将/**放在最为下边

        Object cloudServer = env.getProperty("spring.cloud.nacos.discovery.server-addr");
        filterMap.put("casFilter", casFilter);
        filterMap.put("jwt", new JwtFilter(cloudServer == null, cacheService));
        filterChainDefinitionMap.put("/cas/login", "casFilter");
        filterChainDefinitionMap.put("/**", "jwt");
        log.debug("shiro filter urls: {}", filterChainDefinitionMap);
        // 未授权界面返回JSON
//        shiroFilterFactoryBean.setUnauthorizedUrl("/user/aa");
//        shiroFilterFactoryBean.setLoginUrl("/sys/common/403");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    @Bean(name = "casFilter")
    public CasFilter getCasFilter() {
        CasFilter casFilter = new CasFilter();
        casFilter.setName("casFilter");
        casFilter.setEnabled(true);
        // 登录失败后跳转的URL，也就是 Shiro 执行 CasRealm 的 doGetAuthenticationInfo 方法向CasServer验证tiket
        casFilter.setFailureUrl(casServerUrlPrefix + "/sltCas/login");
//        casFilter.setSuccessUrl(redirectUrl);
        return casFilter;
    }

    @Bean("myModularRealmAuthenticator")
    public MyModularRealmAuthenticator modularRealmAuthenticator() {
        MyModularRealmAuthenticator customizedModularRealmAuthenticator = new MyModularRealmAuthenticator();
        // 设置realm判断条件
        customizedModularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());

        return customizedModularRealmAuthenticator;
    }
    @Bean("securityManager")
//    public DefaultWebSecurityManager securityManager(ShiroRealm myRealm) {
    public DefaultWebSecurityManager securityManager(ShiroCasRealm casRealm, ShiroRealm myRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        List<Realm> realms = new ArrayList<>();
        realms.add(casRealm);
        realms.add(myRealm);
        securityManager.setAuthenticator(modularRealmAuthenticator());
        securityManager.setRealms(realms);
        /*
         * 关闭shiro自带的session，详情见文档
         * http://shiro.apache.org/session-management.html#SessionManagement-
         * StatelessApplications%28Sessionless%29
         */
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        securityManager.setSubjectDAO(subjectDAO);
        return securityManager;
    }

    /**
     * 下面的代码是添加注解支持
     * 
     * @return
     */
//    @Bean
//    @DependsOn("lifecycleBeanPostProcessor")
//    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
//        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
//        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
//        /**
//         * 解决重复代理问题 github#994 添加前缀判断 不匹配 任何Advisor
//         */
//        defaultAdvisorAutoProxyCreator.setUsePrefix(true);
//        defaultAdvisorAutoProxyCreator.setAdvisorBeanNamePrefix("_no_advisor");
//        return defaultAdvisorAutoProxyCreator;
//    }

    @Bean
    public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    @Bean
    public ShiroRealm createShiroRealm() {
        return new ShiroRealm();
    }

    @Bean
    public ShiroCasRealm createShiroCasRealm() {
        return new ShiroCasRealm(casServerUrlPrefix + "/sltCas", casService);
    }

}
